﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Net;
using FeedDotNet.Common;
using FeedDotNet;

namespace ConsoleApplication
{
    class FeedFetcher
    {
        private struct queueData
        {
            public IFeedProvider feedProvider;
            public int retryCount;
            public ManualResetEvent mse;

            public queueData(IFeedProvider feedProvider)
            {
                this.retryCount = 0;
                this.feedProvider = feedProvider;
                this.mse = new ManualResetEvent(false);
            }
        }

        const int SIMUL_REQUEST_COUNT = 5;
        const int MAX_RETRY_COUNT = 3;

        private Queue<queueData> requestQueue;
        private bool fetched = false;

        public FeedFetcher(List<IFeedProvider> providers)
        {
            requestQueue = new Queue<queueData>();

            foreach (IFeedProvider feedProvider in providers)
            {
                requestQueue.Enqueue(new queueData(feedProvider));
            }
        }

        public void FetchFeeds()
        {
            if (fetched)
                return;

            int total = requestQueue.Count;

            while (true)
            {
                if (requestQueue.Count > 0)
                {
                    List<queueData> requestedItems = new List<queueData>();

                    for (int i = 0; i < SIMUL_REQUEST_COUNT  && requestQueue.Count > 0; i++)
                    {
                        lock (requestQueue)
                        {
                            queueData data = requestQueue.Dequeue();
                            data.mse.Reset();

                            requestedItems.Add(data);

                            WebClient webClient = new WebClient();
                            webClient.Proxy = WebProxy.GetDefaultProxy();
                            webClient.DownloadDataCompleted += new DownloadDataCompletedEventHandler(webClient_DownloadDataCompleted);
                            webClient.DownloadDataAsync(data.feedProvider.FeedUri, data);
                        }
                    }

                    foreach (queueData qData in requestedItems)
                    {
                        while (!qData.mse.WaitOne(1000))
                        {
                            Console.Write(string.Format("\rwaiting on feed {2} {0}/{1} to dl... ",requestedItems.Count, total, qData.feedProvider.Key));
                        }
                    }
                }
                else
                {
                    Console.WriteLine("\n");
                    break;
                }
            }
            fetched = true;
        }

        void webClient_DownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e)
        {
            queueData qData = (queueData)e.UserState;
            
            try
            {
                Feed newFeed = null;

                if (e.Error == null)
                {
                    newFeed = FeedReader.ReadString(Encoding.ASCII.GetString(e.Result));
                    qData.feedProvider.PreProcess(ref newFeed);
                }

                if (newFeed == null)
                {
                    qData.retryCount++;
                    if (qData.retryCount < MAX_RETRY_COUNT)
                    {
                        lock (requestQueue)
                        {
                            requestQueue.Enqueue(qData);
                        }
                    }
                }
                else
                {
                    qData.feedProvider.FullFeed = newFeed;
                    qData.feedProvider.PostProcess();
                }
                //this must be after to make sure requestqueue will be serviced sexually...
                qData.mse.Set();
            }
            catch (Exception error)
            {
                Console.WriteLine("Problem Dwonloaed Feed data");
                Console.WriteLine(error.ToString());
                qData.mse.Set();
            }
        }
    }
}
